1 R Setup and Required Packages

In this project, the open-source R programming language is used to model/monitor the changes in gait cycle patterns due to fatigue in simulated manual material handling task experiments. R is maintained by an international team of developers who make the language available at The Comprehensive R Archive Network. Readers interested in reusing our code and reproducing our results should have R installed locally on their machines. R can be installed on a number of different operating systems (see Windows, Mac, and Linux for the installation instructions for these systems). We also recommend using the RStudio interface for R. The reader can download RStudio for free by following the instructions at the link. For non-R users, we recommend the Hands-on Programming with R for a brief overview of the software’s functionality. Hereafter, we assume that the reader has an introductory understanding of the R programming language.

In the code chunk below, we load the packages used to support our analysis. Note that the code of this and any of the code chunks can be hidden by clicking on the ‘Hide’ button to facilitate the navigation. The reader can hide all code and/or download the Rmd file associated with this document by clicking on the Code button on the top right corner of this document. Our input and output files can also be accessed/ downloaded from saebragani/Gait_EWMA.

# check if packages are not installed; if yes, install missing packages
packages = c("tidyverse", "magrittr", # typical data analysis packages
             "MALDIquant", # match closest points between two vectors
             "foreach", "doParallel", "parallel", # packages for parallelization
             "R.matlab", # to read mat files
             "fda.usc", "MFHD", "qcc") # for depth and EWMA calculations
newPackages = packages[!(packages %in% installed.packages()[,"Package"])]
if(length(newPackages) > 0) install.packages(newPackages)

# using the library command to load all packages; invisible used to avoid printing all packages and dependencies used
invisible(lapply(packages, library, character.only = TRUE))

source("./Functions.R") # custom built and modified

2 Gait Acceleration Data and Rational Subgroups

Fourteen participants were equipped with an IMU on their right ankle and performed a three hour manual material handling (MMH) task. The participants loaded weighted boxes on a dolly, pushed the dolly on a set path of 80 m, and unloaded the boxes at the starting location to complete one walking cycle. The IMU signals were collected during the 3-hour sessions. We converted the raw IMU acceleration signals from the local to the global reference frame and removed the gravity. The acceleration signals were then transformed back to the local reference frame. Sagittal acceleration, lateral acceleration, and acceleration magnitude signals were then calculated for further analysis. We used the vertical acceleration component in the global reference frame to segment the gait cycles in order to isolate individual gait cycles. Segmented gait acceleration profiles along with the experimental time stamps were stored in mat files in Matlab.

In the following code chunk we load the segmented acceleration mat files. The \(1^{st}\) 10 minutes of the data were considered as warm up period and thus excluded form the analysis. The acceleration profiles of the gait cycles during each walking cycle were grouped into a rational subgroup. We stored the start and end times of the walking cycles in csv files and loaded and used them in the following code chunk. The acceleration profiles within each start and end times to subgroup the gait cycles.

for (id in setdiff(1:15, 13)) {
  #################### Read start and end time of the subgroups
  video <- read.csv(paste0(file="../Data/csvFiles/Sub", id, ".csv"))

  video_leave <- video$Leaves
  video_leave <- video_leave[!is.na(video_leave)]
  video_leave <- video_leave - 600
  video_leave <- video_leave[video_leave > 0]

  video_enter <- video$Enters
  video_enter <- video_enter[!is.na(video_enter)]
  video_enter <- video_enter - 600
  video_enter <- video_enter[video_enter > 0]

  ################################################### Read from mat files
  raw1 <- readMat(paste0("../Data/matFiles/Subject", id, "_aZ_seg.mat"))

  raw2 <- raw1$gait

  num_rows <- length(raw2)/5

  aM <- list("vector")
  aS <- list("vector")
  aL <- list("vector")
  exp_time <- c()
  for (i in 1:num_rows) {
    aM[[i]] <- raw2[[1*num_rows + i]][[1]][1,]
    aS[[i]] <- raw2[[2*num_rows + i]][[1]][1,]
    aL[[i]] <- raw2[[3*num_rows + i]][[1]][1,]
    exp_time[i] <- raw2[[i+4*num_rows]][[1]][1,1]
  }

  ###################################################### Create subgroups
  if (video_leave[1] > video_enter[1]){
    video_leave <- c(exp_time[1], video_leave)
  }

  if (tail(video_leave, 1) > tail(video_enter, 1)){
    video_enter <- c(video_enter, tail(exp_time, 1))
  }

  conf <- 0.05 * mean(video_enter - video_leave) # 5% confidence for each subgroup

  # Match video subroup times against the experimental times from mat files
  leave_index <- match.closest(video_leave + conf, exp_time)
  enter_index <- match.closest(video_enter - conf, exp_time)

  aM_list <- list()
  aS_list <- list()
  aL_list <- list()
  t_list <- list()
  for (i in 1:length(leave_index)) {
    aM_list[[i]] <- aM[leave_index[i]:enter_index[i]]
    aS_list[[i]] <- aS[leave_index[i]:enter_index[i]]
    aL_list[[i]] <- aL[leave_index[i]:enter_index[i]]
    t_list[[i]] <- exp_time[leave_index[i]:enter_index[i]]
  }

  len <- c()
  for (i in 1:length(aM_list)) {
    len <- c(len, lengths(aM_list[[i]]))
  }

  cut_len <- quantile(len, 0.02)

  for (i in 1:length(aM_list)) {
    remove_ind <- which(lengths(aM_list[[i]]) < cut_len)
    remove_ind <- c(remove_ind, 3000)
    aM_list[[i]] <- aM_list[[i]][-remove_ind]
    aS_list[[i]] <- aS_list[[i]][-remove_ind]
    aL_list[[i]] <- aL_list[[i]][-remove_ind]
    t_list[[i]] <- t_list[[i]][-remove_ind]
  }

  batchSize <- lengths(aM_list)

  ###############################################
  aM_list_all <- do.call(c, aM_list)
  aS_list_all <- do.call(c, aS_list)
  aL_list_all <- do.call(c, aL_list)
  t_all <- do.call(c, t_list)
  ###############################################
  aM_mat <- do.call(rbind, aM_list_all)[,1:cut_len]
  aS_mat <- do.call(rbind, aS_list_all)[,1:cut_len]
  aL_mat <- do.call(rbind, aL_list_all)[,1:cut_len]

  ##################
  assign(paste0("sub", id, "_aMSLT"),
         list(aMag=aM_mat, aSag=aS_mat, aLat=aL_mat, time=t_all, batchSize=batchSize))
}

########################################### Save
save(sub1_aMSLT, sub2_aMSLT, sub3_aMSLT, sub4_aMSLT, sub5_aMSLT, sub6_aMSLT,
     sub7_aMSLT, sub8_aMSLT, sub9_aMSLT, sub10_aMSLT, sub11_aMSLT, sub12_aMSLT,
     sub14_aMSLT, sub15_aMSLT,

     file="../Data/rData/subGs_for_depth.Rdata")

3 Depth Calculation

The depth measure investigates how deep a data point is with respect to a sample. In univariate data the median represents the deepest point in a cloud of points. In functional data analysis (FDA) depth measure is commonly used for classification and clustering of curves. In multivariate data where a number of discretized curves in a certain number of points (usually time points) are investigated, the depth measures the centrality or outlyingness of each curve. However, for multivariate data there is no obvious simple method for depth measurement. Multiple depth measures have been proposed in literature Cuevas et al. (2007). These depth values are defined based on median Fraiman & Muniz (2001), Tukey depth, random projections of the curves and their projections.

In the current work we have exploited the depth measure to investigate the centrality of the gait cycle acceleration profiles in order to find their deviation from the baseline due to fatigue. After investigating multiple depth measures Lamooki et al. (n.d.), we have narrowed them down to mode depth @Cuevas et al. (2007) and Multivariate functional halfspace depth (MFHD) Hubert et al. (2012).

3.1 Mode Depth

In the following code chunk we have calculated the mode depth for each gait cycle acceleration profile using the fda.usc package in R Febrero Bande & Oviedo de la Fuente (2012). The mode depth is calculated for the acceleration magnitude by using the \(1^{st}\) 500 gait cycles as the baseline. The depth of the remaining gait cycle acceleration profiles are calculated against this baseline.

load(file="../Data/rData/subGs_for_depth.Rdata")

for (id in setdiff(1:15, 13)) {
  
  aMag <- get(paste0("sub", id, "_aMSLT"))$aMag
  tExp <- get(paste0("sub", id, "_aMSLT"))$time
  
  ################################## Mode Depth
  inControl <- fdata(aMag[1:500,])
  online <- fdata(aMag[501:nrow(aMag),])
  
  inControlDepth <- {depth.mode(inControl)}$dep
  onlineDepth <- {depth.mode(fdataobj=online, fdataori=inControl)}$dep
  
  modeDepth <- c(inControlDepth, onlineDepth)
  
  assign(paste0("sub", id, "_mode_mag"),
         list(modeDepth=modeDepth, tExp=tExp))
}

save(sub1_mode_mag, sub2_mode_mag, sub3_mode_mag, sub4_mode_mag, sub5_mode_mag, sub6_mode_mag,
     sub7_mode_mag, sub8_mode_mag, sub9_mode_mag, sub10_mode_mag, sub11_mode_mag, sub12_mode_mag, sub14_mode_mag, sub15_mode_mag,
     file="../Data/rData/mode_mag.Rdata")

3.2 Visualize the Mode Depth

The Mode depth for every gait cycle is visualized here where the red data points represent the baseline profiles.

load(file="../Data/rData/mode_mag.Rdata")

for (id in setdiff(1:15, 13)) {
  depthMode <- get(paste0("sub", id, "_mode_mag"))$modeDepth
  tMode <- get(paste0("sub", id, "_mode_mag"))$tExp

  cat("###", paste0("Subject", id), "{-}",'\n')
  plot(tMode, depthMode, pch=16, cex=1,col=c(rep("red", 500), rep("black", length(depthMode)-500)))
  legend("topright", legend=c("Baseline data", "New data"), col=c("red", "black"), pch=16)
  cat('\n \n')

}

Subject1

Subject2

Subject3

Subject4

Subject5

Subject6

Subject7

Subject8

Subject9

Subject10

Subject11

Subject12

Subject14

Subject15

3.3 Multivariate Functional Halfspace Depth (MFHD)

The MFHD depth is introduced to provide ordering of curves from the center outwards for multivariate curves Hubert et al. (2012). A multivariate curve contains a multivariate vector at each discrete time point. The method is particularly used on multivariate curves that are constructed by adding univariate curves and their derivatives. However, in the current work we have implemented the method on multivariate curves constructed by sagittal and lateral gait acceleration profiles. The \(1^{st}\) 500 gait profiles were considered to be the baseline data where the participant was walking fatigue-free. Next, each of the remaining gait acceleration profile was added to the baseline profiles and their MFHD depth value were calculated.

The following code chunk includes the code that we have used to calculate th MFHD depth using the MFHD function in R. Since the MFHD depth computations are significantly time consuming, we have performed the computations in parallel on a supercomputing cluster with 40 cores and set the eval option to FALSE here. The output of this code chunk is added to the folder Data/rData.

load(file="../Data/rData/subGs_for_depth.Rdata")

cores = detectCores() - 2 # to give the server some breathing Room
cl = makePSOCKcluster(cores)
registerDoParallel(cl)

for (id in setdiff(1:15, 13)) {
  
  aSag <- get(paste0("sub", id, "_aMSLT"))$aSag
  aLat <- get(paste0("sub", id, "_aMSLT"))$aLat
  tExp <- get(paste0("sub", id, "_aMSLT"))$time
  
  ################################## Mode Depth
  inControlSag <- aSag[1:500,]
  inControlLat <- aLat[1:500,]
  inControlDepth <- {MFHD(y1=inControlSag, y2=inControlLat, alpha=0.125, Beta=0.5)}$MFHDdepth[1,]
  
  ###################### foreach
  onlineDepth <- c()
  end <- nrow(aSag)
  onlineDepth <- foreach(i=501:end, .packages = c('fda.usc', 'tidyverse', 'MFHD'),
                         .combine='c') %dopar% {
                           temp1 <- aSag %>% .[i,]
                           temp2 <- aLat %>% .[i,]
                           
                           append1 <- rbind(inControlSag, temp1)
                           append2 <- rbind(inControlLat, temp2)
                           
                           temp3 <- MFHD(y1=append1, y2=append2, alpha=0.125, Beta=0.5)
                           
                           temp4 <- temp3$MFHDdepth %>% .[1,501]
                           temp4
                         }
  
  MFHDdepth <- c(inControlDepth, onlineDepth)
  
  assign(paste0("sub", id, "_MFHD"),
         list(MFHDdepth=MFHDdepth, tExp=tExp))
}

save(sub1_MFHD, sub2_MFHD, sub3_MFHD, sub4_MFHD, sub5_MFHD, sub6_MFHD, sub7_MFHD, sub8_MFHD,
     sub9_MFHD, sub10_MFHD, sub11_MFHD, sub12_MFHD, sub14_MFHD, sub15_MFHD,
     file="../Data/rData/MFHD.Rdata")

3.4 Visualize the MFHD Depth

Below we have visualized the MFHD depth for the gait cycles where the red data points represent the baseline gaits.

load(file="../Data/rData/MFHD.Rdata")

for (id in setdiff(1:15, 13)) {
  depthMFHD <- get(paste0("sub", id, "_MFHD"))$MFHDdepth
  tMFHD <- get(paste0("sub", id, "_MFHD"))$tExp
  
  cat("###", paste0("Subject", id), "{-}",'\n')
  plot(tMFHD, depthMFHD, pch=16, cex=1,col=c(rep("red", 500), rep("black", length(depthMFHD)-500)))
  legend("topright", legend=c("Baseline data", "New data"), col=c("red", "black"),
         pch=16)
  cat('\n \n')
  
}

Subject1

Subject2

Subject3

Subject4

Subject5

Subject6

Subject7

Subject8

Subject9

Subject10

Subject11

Subject12

Subject14

Subject15

4 Exponentially Weighted Moving Average (EWMA)

Cumulative SPC methods are designed to detect small and persistent changes in the data by using the observed data available at the current time point. Such methods work by accumulating the small changes in the controlled statistics that occurred prior to the present time point. Since we expected the gait data in our study to change gradually, we used the exponentially weighted moving average (EWMA) SPC method to monitor fatigue during the simulated MMH sessions.

4.1 EWMA on Subgroup Mode Depth Values

After calculating the Mode depth values for individual gait cycles we reassigned the depth values to their original subgroups in the following code chunk. Next, we used the qcc package in R and constructed the EWMA control chart by considering the \(1^{st}\) 10 subgroups as the in-control data and presented the control charts.

load(file="../Data/rData/subGs_for_depth.Rdata")
load(file="../Data/rData/mode_mag.Rdata")
aa <- 3
for (id in setdiff(1:15, 13)) {
  
  dep_Mode <- get(paste0("sub", id, "_mode_mag"))$modeDepth
  batchSize <- get(paste0("sub", id, "_aMSLT"))$batchSize
  
  ################################################ reassign depth values to original subGroups
  depMode_split <- sapply(1:length(batchSize), FUN=function(x){rep(x, batchSize[x])}) %>% do.call(what=c) %>% split(x=dep_Mode)
  
  ####################################### Create matrix with NA's at the end of smaller batches
  Mode_batch_mat <- sapply(1:length(depMode_split), FUN=function(x){c(depMode_split[[x]], rep(NA, max(batchSize) - length(depMode_split[[x]])))}) %>% t()
  
  ####################################### Remove small length batches
  rm_ind <- which(apply(Mode_batch_mat, 1, function(x) sum(!is.na(x))) < 10)
  rm_ind <- c(rm_ind, 3000)
  
  Mode_batch_mat <- Mode_batch_mat[-rm_ind,]
  
  ####################################### Plot MFHD EWMA for walking cycle batches
  Lambda <- 0.5
  end <- nrow(Mode_batch_mat)
  IC_len <- 10
  
  ewma1 <- ewma(Mode_batch_mat[1:IC_len,], lambda=Lambda, nsigmas=5, newdata=Mode_batch_mat[(IC_len+1):end,], add.stats=F, plot=F)
  
  cat("###", paste0("Subject", id), "{-}",'\n')
  plot(ewma1,
       title=paste0("subject ", id, "\nEWMA Chart on Mode Depth"), xlab="Subgroup Index")
  cat('\n \n')
}

Subject1

Subject2

Subject3

Subject4

Subject5

Subject6

Subject7

Subject8

Subject9

Subject10

Subject11

Subject12

Subject14

Subject15

4.2 EWMA on Subgroup EWMA Depth Values

In the following code chunk we have constructed the EWMA control charts on the MFHD depth values using the qcc package in R and presented the charts.

load(file="../Data/rData/subGs_for_depth.Rdata")
load(file="../Data/rData/MFHD.Rdata")

aa <- 2

for (id in setdiff(1:15, 13)) {
  
  dep_MFHD <- get(paste0("sub", id, "_MFHD"))$MFHDdepth
  batchSize <- get(paste0("sub", id, "_aMSLT"))$batchSize
  
  ################################################ reassign depth values to original subGroups
  depMFHD_split <- sapply(1:length(batchSize), FUN=function(x){rep(x, batchSize[x])}) %>% do.call(what=c) %>% split(x=dep_MFHD)
  
  ####################################### Create matrix with NA's at the end of smaller batches
  MFHD_batch_mat <- sapply(1:length(depMFHD_split), FUN=function(x){c(depMFHD_split[[x]], rep(NA, max(batchSize) - length(depMFHD_split[[x]])))}) %>% t()
  
  ####################################### Remove small length batches
  rm_ind <- which(apply(MFHD_batch_mat, 1, function(x) sum(!is.na(x))) < 10)
  rm_ind <- c(rm_ind, 3000)
  
  MFHD_batch_mat <- MFHD_batch_mat[-rm_ind,]
  
  ####################################### Plot MFHD EWMA for walking cycle batches
  Lambda <- 0.5
  end <- nrow(MFHD_batch_mat)
  IC_len <- 10
  
  ewma1 <- ewma(MFHD_batch_mat[1:IC_len,], lambda=Lambda, nsigmas=5, newdata=MFHD_batch_mat[(IC_len+1):end,], add.stats=F, plot=F)
  
  cat("###", paste0("Subject", id), "{-}",'\n')
  plot(ewma1,
       title=paste0("subject ", id, "\nEWMA Chart on MFHD Depth"), xlab="Subgroup Index")
  cat('\n \n')
}

Subject1

Subject2

Subject3

Subject4

Subject5

Subject6

Subject7

Subject8

Subject9

Subject10

Subject11

Subject12

Subject14

Subject15


References

Cuevas, A., Febrero, M., & Fraiman, R. (2007). Robust estimation and classification for functional data via projection-based depth notions. Computational Statistics, 22(3), 481–496.

Febrero Bande, M., & Oviedo de la Fuente, M. (2012). Statistical computing in functional data analysis: The r package fda. Usc.

Fraiman, R., & Muniz, G. (2001). Trimmed means for functional data. Test, 10(2), 419–440.

Hubert, M., Claeskens, G., De Ketelaere, B., & Vakili, K. (2012). A new depth-based approach for detecting outlying curves. Proceedings of Compstat, 329–340.

Lamooki, S. R., Kang, J., Cavuoto, L. A., Megahed, F. M., & Jones-Farmer, L. A. (n.d.). Challenges and opportunities for statistical monitoring of gait cycle acceleration observed from imu data for fatigue detection. 2020 8th Ieee Ras/Embs International Conference for Biomedical Robotics and Biomechatronics (Biorob), 593–598.


  1. Email: | Website: LinkedIn↩︎

  2. Email: | Phone: +1-716-645-6063 | Website: University at Buffalo Official↩︎

  3. Email: | Phone: +1-716-645-4696 | Website: University at Buffalo Official↩︎

  4. Email: | Phone: +1-513-529-4185 | Website: Miami University Official↩︎

  5. Email: | Phone: +1-513-529-4823 | Website: Miami University Official↩︎

LS0tDQp0aXRsZTogIkEgUGVyc29uYWxpemVkIFN0YXRpc3RpY2FsIEFwcHJvYWNoIGZvciBEZXRlY3RpbmcgQ2hhbmdlcyBpbiBHYWl0IEN5Y2xlcyBEdWUgdG8gRmF0aWd1ZSINCmF1dGhvcjoNCiAgLSBuYW1lOiAiU2FlYiBSYWdhbmkgTGFtb29raSBeW0VtYWlsOiBzYWVicmFnYUBidWZmYWxvLmVkdSB8IFdlYnNpdGU6IDxhIGhyZWY9XCJodHRwczovL3d3dy5saW5rZWRpbi5jb20vaW4vc2FlYi1yYWdhbmktbGFtb29raS0xMjNhOTY1OC9cIj5MaW5rZWRJbjwvYT5dIg0KICAgIGFmZmlsaWF0aW9uOiBEZXBhcnRtZW50IG9mIE1lY2hhbmljYWwgJiBBZXJvc3BhY2UgRW5naW5lZXJpbmcsIFVuaXZlcnNpdHkgYXQgQnVmZmFsbw0KICAtIG5hbWU6ICJKaXllb24gS2FuZyBeW0VtYWlsOiBqaXllb25rQGJ1ZmZhbG8uZWR1IHwgUGhvbmU6ICsxLTcxNi02NDUtNjA2MyB8IFdlYnNpdGU6IDxhIGhyZWY9XCJodHRwOi8vZW5naW5lZXJpbmcuYnVmZmFsby5lZHUvaW5kdXN0cmlhbC1zeXN0ZW1zL3Blb3BsZS9mYWN1bHR5LWRpcmVjdG9yeS9qLWphbmcuaHRtbFwiPlVuaXZlcnNpdHkgYXQgQnVmZmFsbyBPZmZpY2lhbDwvYT5dIg0KICAgIGFmZmlsaWF0aW9uOiBEZXBhcnRtZW50IG9mIE1lY2hhbmljYWwgJiBBZXJvc3BhY2UgRW5naW5lZXJpbmcsIFVuaXZlcnNpdHkgYXQgQnVmZmFsbw0KICAtIG5hbWU6ICJMb3JhIEEuIENhdnVvdG8gXltFbWFpbDogbG9yYWNhdnVAYnVmZmFsby5lZHUgfCBQaG9uZTogKzEtNzE2LTY0NS00Njk2IHwgV2Vic2l0ZTogPGEgaHJlZj1cImh0dHA6Ly9lbmdpbmVlcmluZy5idWZmYWxvLmVkdS9pbmR1c3RyaWFsLXN5c3RlbXMvcGVvcGxlL2ZhY3VsdHktZGlyZWN0b3J5L2NhdnVvdG8tbG9yYS5odG1sXCI+VW5pdmVyc2l0eSBhdCBCdWZmYWxvIE9mZmljaWFsPC9hPl0iDQogICAgYWZmaWxpYXRpb246IERlcGFydG1lbnQgb2YgSW5kdXN0cmlhbCBhbmQgU3lzdGVtcyBFbmdpbmVlcmluZywgVW5pdmVyc2l0eSBhdCBCdWZmYWxvDQogIC0gbmFtZTogIkZhZGVsIE0uIE1lZ2FoZWQgXltFbWFpbDogZm1lZ2FoZWRAbWlhbWlvaC5lZHUgfCBQaG9uZTogKzEtNTEzLTUyOS00MTg1IHwgV2Vic2l0ZTogPGEgaHJlZj1cImh0dHBzOi8vbWlhbWlvaC5lZHUvZnNiL2RpcmVjdG9yeS8/dXA9L2RpcmVjdG9yeS9tZWdhaGVmbVwiPk1pYW1pIFVuaXZlcnNpdHkgT2ZmaWNpYWw8L2E+XSINCiAgICBhZmZpbGlhdGlvbjogRmFybWVyIFNjaG9vbCBvZiBCdXNpbmVzcywgTWlhbWkgVW5pdmVyc2l0eQ0KICAtIG5hbWU6ICJBbGxpc29uIEpvbmVzIEZhcm1lciBeW0VtYWlsOiBmYXJtZXJsMkBtaWFtaW9oLmVkdSB8IFBob25lOiArMS01MTMtNTI5LTQ4MjMgfCBXZWJzaXRlOiA8YSBocmVmPVwiaHR0cHM6Ly9taWFtaW9oLmVkdS9mc2IvZGlyZWN0b3J5Lz91cD0vZGlyZWN0b3J5L2Zhcm1lcmwyXCI+TWlhbWkgVW5pdmVyc2l0eSBPZmZpY2lhbDwvYT5dIg0KICAgIGFmZmlsaWF0aW9uOiBGYXJtZXIgU2Nob29sIG9mIEJ1c2luZXNzLCBNaWFtaSBVbml2ZXJzaXR5DQpiaWJsaW9ncmFwaHk6IEVXTUFSZWZzLmJpYg0KY3NsOiBhcGEuY3NsDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiAlZCwgJVknKWAiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KICAgIG51bWJlcl9zZWN0aW9uczogVFJVRQ0KICAgIHRoZW1lOiBzaW1wbGV4DQogICAgcGFnZWRfZGY6IFRSVUUNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQogIGluY2x1ZGVzOg0KICAgIGluX2hlYWRlcjogc3RydWN0dXJlLnRleA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY2FjaGUgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgIHByb2dyZXNzID0gRkFMU0UsIA0KICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBkcGkgPSA2MDApDQpvcHRpb25zKHF3cmFwczJfbWFya3VwID0gIm1hcmtkb3duIikNCg0KDQpgYGANCg0KLS0tDQoNCiMgUiBTZXR1cCBhbmQgUmVxdWlyZWQgUGFja2FnZXMNCg0KSW4gdGhpcyBwcm9qZWN0LCB0aGUgb3Blbi1zb3VyY2UgUiBwcm9ncmFtbWluZyBsYW5ndWFnZSBpcyB1c2VkIHRvIG1vZGVsL21vbml0b3IgdGhlIGNoYW5nZXMgaW4gZ2FpdCBjeWNsZSBwYXR0ZXJucyBkdWUgdG8gZmF0aWd1ZSBpbiBzaW11bGF0ZWQgbWFudWFsIG1hdGVyaWFsIGhhbmRsaW5nIHRhc2sgZXhwZXJpbWVudHMuIFIgaXMgbWFpbnRhaW5lZCBieSBhbiBpbnRlcm5hdGlvbmFsIHRlYW0gb2YgZGV2ZWxvcGVycyB3aG8gbWFrZSB0aGUgbGFuZ3VhZ2UgYXZhaWxhYmxlIGF0IFtUaGUgQ29tcHJlaGVuc2l2ZSBSIEFyY2hpdmUgTmV0d29ya10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvKS4gUmVhZGVycyBpbnRlcmVzdGVkIGluIHJldXNpbmcgb3VyIGNvZGUgYW5kIHJlcHJvZHVjaW5nIG91ciByZXN1bHRzIHNob3VsZCBoYXZlIFIgaW5zdGFsbGVkIGxvY2FsbHkgb24gdGhlaXIgbWFjaGluZXMuIFIgY2FuIGJlIGluc3RhbGxlZCBvbiBhIG51bWJlciBvZiBkaWZmZXJlbnQgb3BlcmF0aW5nIHN5c3RlbXMgKHNlZSBbV2luZG93c10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvYmluL3dpbmRvd3MvKSwgW01hY10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvYmluL21hY29zeC8pLCBhbmQgW0xpbnV4XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9iaW4vbGludXgvKSBmb3IgdGhlIGluc3RhbGxhdGlvbiBpbnN0cnVjdGlvbnMgZm9yIHRoZXNlIHN5c3RlbXMpLiBXZSBhbHNvIHJlY29tbWVuZCB1c2luZyB0aGUgUlN0dWRpbyBpbnRlcmZhY2UgZm9yIFIuIFRoZSByZWFkZXIgY2FuIFtkb3dubG9hZCBSU3R1ZGlvXShodHRwOi8vd3d3LnJzdHVkaW8uY29tL2lkZSkgZm9yIGZyZWUgYnkgZm9sbG93aW5nIHRoZSBpbnN0cnVjdGlvbnMgYXQgdGhlIGxpbmsuIEZvciBub24tUiB1c2Vycywgd2UgcmVjb21tZW5kIHRoZSBbSGFuZHMtb24gUHJvZ3JhbW1pbmcgd2l0aCBSXShodHRwczovL3JzdHVkaW8tZWR1Y2F0aW9uLmdpdGh1Yi5pby9ob3ByL3BhY2thZ2VzLmh0bWwpIGZvciBhIGJyaWVmIG92ZXJ2aWV3IG9mIHRoZSBzb2Z0d2FyZSdzIGZ1bmN0aW9uYWxpdHkuIEhlcmVhZnRlciwgd2UgYXNzdW1lIHRoYXQgdGhlIHJlYWRlciBoYXMgYW4gaW50cm9kdWN0b3J5IHVuZGVyc3RhbmRpbmcgb2YgdGhlIFIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UuDQoNCkluIHRoZSBjb2RlIGNodW5rIGJlbG93LCB3ZSBsb2FkIHRoZSBwYWNrYWdlcyB1c2VkIHRvIHN1cHBvcnQgb3VyIGFuYWx5c2lzLiBOb3RlIHRoYXQgdGhlIGNvZGUgb2YgdGhpcyBhbmQgYW55IG9mIHRoZSBjb2RlIGNodW5rcyBjYW4gYmUgaGlkZGVuIGJ5IGNsaWNraW5nIG9uIHRoZSAnSGlkZScgYnV0dG9uIHRvIGZhY2lsaXRhdGUgdGhlIG5hdmlnYXRpb24uICoqVGhlIHJlYWRlciBjYW4gaGlkZSBhbGwgY29kZSBhbmQvb3IgZG93bmxvYWQgdGhlIFJtZCBmaWxlIGFzc29jaWF0ZWQgd2l0aCB0aGlzIGRvY3VtZW50IGJ5IGNsaWNraW5nIG9uIHRoZSBDb2RlIGJ1dHRvbiBvbiB0aGUgdG9wIHJpZ2h0IGNvcm5lciBvZiB0aGlzIGRvY3VtZW50LioqIE91ciBpbnB1dCBhbmQgb3V0cHV0IGZpbGVzIGNhbiBhbHNvIGJlIGFjY2Vzc2VkLyBkb3dubG9hZGVkIGZyb20gW3NhZWJyYWdhbmkvR2FpdF9FV01BXShodHRwczovL2dpdGh1Yi5jb20vc2FlYnJhZ2FuaS9HYWl0X0VXTUEpLg0KDQpgYGB7ciBwYWNrYWdlcywgY2FjaGU9RkFMU0V9DQoNCiMgY2hlY2sgaWYgcGFja2FnZXMgYXJlIG5vdCBpbnN0YWxsZWQ7IGlmIHllcywgaW5zdGFsbCBtaXNzaW5nIHBhY2thZ2VzDQpwYWNrYWdlcyA9IGMoInRpZHl2ZXJzZSIsICJtYWdyaXR0ciIsICMgdHlwaWNhbCBkYXRhIGFuYWx5c2lzIHBhY2thZ2VzDQogICAgICAgICAgICAgIk1BTERJcXVhbnQiLCAjIG1hdGNoIGNsb3Nlc3QgcG9pbnRzIGJldHdlZW4gdHdvIHZlY3RvcnMNCiAgICAgICAgICAgICAiZm9yZWFjaCIsICJkb1BhcmFsbGVsIiwgInBhcmFsbGVsIiwgIyBwYWNrYWdlcyBmb3IgcGFyYWxsZWxpemF0aW9uDQogICAgICAgICAgICAgIlIubWF0bGFiIiwgIyB0byByZWFkIG1hdCBmaWxlcw0KICAgICAgICAgICAgICJmZGEudXNjIiwgIk1GSEQiLCAicWNjIikgIyBmb3IgZGVwdGggYW5kIEVXTUEgY2FsY3VsYXRpb25zDQpuZXdQYWNrYWdlcyA9IHBhY2thZ2VzWyEocGFja2FnZXMgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssIlBhY2thZ2UiXSldDQppZihsZW5ndGgobmV3UGFja2FnZXMpID4gMCkgaW5zdGFsbC5wYWNrYWdlcyhuZXdQYWNrYWdlcykNCg0KIyB1c2luZyB0aGUgbGlicmFyeSBjb21tYW5kIHRvIGxvYWQgYWxsIHBhY2thZ2VzOyBpbnZpc2libGUgdXNlZCB0byBhdm9pZCBwcmludGluZyBhbGwgcGFja2FnZXMgYW5kIGRlcGVuZGVuY2llcyB1c2VkDQppbnZpc2libGUobGFwcGx5KHBhY2thZ2VzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKQ0KDQpzb3VyY2UoIi4vRnVuY3Rpb25zLlIiKSAjIGN1c3RvbSBidWlsdCBhbmQgbW9kaWZpZWQNCmBgYA0KDQotLS0NCg0KIyBHYWl0IEFjY2VsZXJhdGlvbiBEYXRhIGFuZCBSYXRpb25hbCBTdWJncm91cHMNCg0KRm91cnRlZW4gcGFydGljaXBhbnRzIHdlcmUgZXF1aXBwZWQgd2l0aCBhbiBJTVUgb24gdGhlaXIgcmlnaHQgYW5rbGUgYW5kIHBlcmZvcm1lZCBhIHRocmVlIGhvdXIgbWFudWFsIG1hdGVyaWFsIGhhbmRsaW5nIChNTUgpIHRhc2suIFRoZSBwYXJ0aWNpcGFudHMgbG9hZGVkIHdlaWdodGVkIGJveGVzIG9uIGEgZG9sbHksIHB1c2hlZCB0aGUgZG9sbHkgb24gYSBzZXQgcGF0aCBvZiA4MCBtLCBhbmQgdW5sb2FkZWQgdGhlIGJveGVzIGF0IHRoZSBzdGFydGluZyBsb2NhdGlvbiB0byBjb21wbGV0ZSBvbmUgd2Fsa2luZyBjeWNsZS4gVGhlIElNVSBzaWduYWxzIHdlcmUgY29sbGVjdGVkIGR1cmluZyB0aGUgMy1ob3VyIHNlc3Npb25zLiBXZSBjb252ZXJ0ZWQgdGhlIHJhdyBJTVUgYWNjZWxlcmF0aW9uIHNpZ25hbHMgZnJvbSB0aGUgbG9jYWwgdG8gdGhlIGdsb2JhbCByZWZlcmVuY2UgZnJhbWUgYW5kIHJlbW92ZWQgdGhlIGdyYXZpdHkuIFRoZSBhY2NlbGVyYXRpb24gc2lnbmFscyB3ZXJlIHRoZW4gdHJhbnNmb3JtZWQgYmFjayB0byB0aGUgbG9jYWwgcmVmZXJlbmNlIGZyYW1lLiBTYWdpdHRhbCBhY2NlbGVyYXRpb24sIGxhdGVyYWwgYWNjZWxlcmF0aW9uLCBhbmQgYWNjZWxlcmF0aW9uIG1hZ25pdHVkZSBzaWduYWxzIHdlcmUgdGhlbiBjYWxjdWxhdGVkIGZvciBmdXJ0aGVyIGFuYWx5c2lzLiBXZSB1c2VkIHRoZSB2ZXJ0aWNhbCBhY2NlbGVyYXRpb24gY29tcG9uZW50IGluIHRoZSBnbG9iYWwgcmVmZXJlbmNlIGZyYW1lIHRvIHNlZ21lbnQgdGhlIGdhaXQgY3ljbGVzIGluIG9yZGVyIHRvIGlzb2xhdGUgaW5kaXZpZHVhbCBnYWl0IGN5Y2xlcy4gU2VnbWVudGVkIGdhaXQgYWNjZWxlcmF0aW9uIHByb2ZpbGVzIGFsb25nIHdpdGggdGhlIGV4cGVyaW1lbnRhbCB0aW1lIHN0YW1wcyB3ZXJlIHN0b3JlZCBpbiBtYXQgZmlsZXMgaW4gTWF0bGFiLg0KDQpJbiB0aGUgZm9sbG93aW5nIGNvZGUgY2h1bmsgd2UgbG9hZCB0aGUgc2VnbWVudGVkIGFjY2VsZXJhdGlvbiBtYXQgZmlsZXMuIFRoZSAkMV57c3R9JCAxMCBtaW51dGVzIG9mIHRoZSBkYXRhIHdlcmUgY29uc2lkZXJlZCBhcyB3YXJtIHVwIHBlcmlvZCBhbmQgdGh1cyBleGNsdWRlZCBmb3JtIHRoZSBhbmFseXNpcy4gVGhlIGFjY2VsZXJhdGlvbiBwcm9maWxlcyBvZiB0aGUgZ2FpdCBjeWNsZXMgZHVyaW5nIGVhY2ggd2Fsa2luZyBjeWNsZSB3ZXJlIGdyb3VwZWQgaW50byBhIHJhdGlvbmFsIHN1Ymdyb3VwLiBXZSBzdG9yZWQgdGhlIHN0YXJ0IGFuZCBlbmQgdGltZXMgb2YgdGhlIHdhbGtpbmcgY3ljbGVzIGluIGNzdiBmaWxlcyBhbmQgbG9hZGVkIGFuZCB1c2VkIHRoZW0gaW4gdGhlIGZvbGxvd2luZyBjb2RlIGNodW5rLiBUaGUgYWNjZWxlcmF0aW9uIHByb2ZpbGVzIHdpdGhpbiBlYWNoIHN0YXJ0IGFuZCBlbmQgdGltZXMgdG8gc3ViZ3JvdXAgdGhlIGdhaXQgY3ljbGVzLg0KDQoNCmBgYHtyIHJlYWQtc3ViZ30NCg0KZm9yIChpZCBpbiBzZXRkaWZmKDE6MTUsIDEzKSkgew0KICAjIyMjIyMjIyMjIyMjIyMjIyMjIyBSZWFkIHN0YXJ0IGFuZCBlbmQgdGltZSBvZiB0aGUgc3ViZ3JvdXBzDQogIHZpZGVvIDwtIHJlYWQuY3N2KHBhc3RlMChmaWxlPSIuLi9EYXRhL2NzdkZpbGVzL1N1YiIsIGlkLCAiLmNzdiIpKQ0KDQogIHZpZGVvX2xlYXZlIDwtIHZpZGVvJExlYXZlcw0KICB2aWRlb19sZWF2ZSA8LSB2aWRlb19sZWF2ZVshaXMubmEodmlkZW9fbGVhdmUpXQ0KICB2aWRlb19sZWF2ZSA8LSB2aWRlb19sZWF2ZSAtIDYwMA0KICB2aWRlb19sZWF2ZSA8LSB2aWRlb19sZWF2ZVt2aWRlb19sZWF2ZSA+IDBdDQoNCiAgdmlkZW9fZW50ZXIgPC0gdmlkZW8kRW50ZXJzDQogIHZpZGVvX2VudGVyIDwtIHZpZGVvX2VudGVyWyFpcy5uYSh2aWRlb19lbnRlcildDQogIHZpZGVvX2VudGVyIDwtIHZpZGVvX2VudGVyIC0gNjAwDQogIHZpZGVvX2VudGVyIDwtIHZpZGVvX2VudGVyW3ZpZGVvX2VudGVyID4gMF0NCg0KICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgUmVhZCBmcm9tIG1hdCBmaWxlcw0KICByYXcxIDwtIHJlYWRNYXQocGFzdGUwKCIuLi9EYXRhL21hdEZpbGVzL1N1YmplY3QiLCBpZCwgIl9hWl9zZWcubWF0IikpDQoNCiAgcmF3MiA8LSByYXcxJGdhaXQNCg0KICBudW1fcm93cyA8LSBsZW5ndGgocmF3MikvNQ0KDQogIGFNIDwtIGxpc3QoInZlY3RvciIpDQogIGFTIDwtIGxpc3QoInZlY3RvciIpDQogIGFMIDwtIGxpc3QoInZlY3RvciIpDQogIGV4cF90aW1lIDwtIGMoKQ0KICBmb3IgKGkgaW4gMTpudW1fcm93cykgew0KICAgIGFNW1tpXV0gPC0gcmF3MltbMSpudW1fcm93cyArIGldXVtbMV1dWzEsXQ0KICAgIGFTW1tpXV0gPC0gcmF3MltbMipudW1fcm93cyArIGldXVtbMV1dWzEsXQ0KICAgIGFMW1tpXV0gPC0gcmF3MltbMypudW1fcm93cyArIGldXVtbMV1dWzEsXQ0KICAgIGV4cF90aW1lW2ldIDwtIHJhdzJbW2krNCpudW1fcm93c11dW1sxXV1bMSwxXQ0KICB9DQoNCiAgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIENyZWF0ZSBzdWJncm91cHMNCiAgaWYgKHZpZGVvX2xlYXZlWzFdID4gdmlkZW9fZW50ZXJbMV0pew0KICAgIHZpZGVvX2xlYXZlIDwtIGMoZXhwX3RpbWVbMV0sIHZpZGVvX2xlYXZlKQ0KICB9DQoNCiAgaWYgKHRhaWwodmlkZW9fbGVhdmUsIDEpID4gdGFpbCh2aWRlb19lbnRlciwgMSkpew0KICAgIHZpZGVvX2VudGVyIDwtIGModmlkZW9fZW50ZXIsIHRhaWwoZXhwX3RpbWUsIDEpKQ0KICB9DQoNCiAgY29uZiA8LSAwLjA1ICogbWVhbih2aWRlb19lbnRlciAtIHZpZGVvX2xlYXZlKSAjIDUlIGNvbmZpZGVuY2UgZm9yIGVhY2ggc3ViZ3JvdXANCg0KICAjIE1hdGNoIHZpZGVvIHN1YnJvdXAgdGltZXMgYWdhaW5zdCB0aGUgZXhwZXJpbWVudGFsIHRpbWVzIGZyb20gbWF0IGZpbGVzDQogIGxlYXZlX2luZGV4IDwtIG1hdGNoLmNsb3Nlc3QodmlkZW9fbGVhdmUgKyBjb25mLCBleHBfdGltZSkNCiAgZW50ZXJfaW5kZXggPC0gbWF0Y2guY2xvc2VzdCh2aWRlb19lbnRlciAtIGNvbmYsIGV4cF90aW1lKQ0KDQogIGFNX2xpc3QgPC0gbGlzdCgpDQogIGFTX2xpc3QgPC0gbGlzdCgpDQogIGFMX2xpc3QgPC0gbGlzdCgpDQogIHRfbGlzdCA8LSBsaXN0KCkNCiAgZm9yIChpIGluIDE6bGVuZ3RoKGxlYXZlX2luZGV4KSkgew0KICAgIGFNX2xpc3RbW2ldXSA8LSBhTVtsZWF2ZV9pbmRleFtpXTplbnRlcl9pbmRleFtpXV0NCiAgICBhU19saXN0W1tpXV0gPC0gYVNbbGVhdmVfaW5kZXhbaV06ZW50ZXJfaW5kZXhbaV1dDQogICAgYUxfbGlzdFtbaV1dIDwtIGFMW2xlYXZlX2luZGV4W2ldOmVudGVyX2luZGV4W2ldXQ0KICAgIHRfbGlzdFtbaV1dIDwtIGV4cF90aW1lW2xlYXZlX2luZGV4W2ldOmVudGVyX2luZGV4W2ldXQ0KICB9DQoNCiAgbGVuIDwtIGMoKQ0KICBmb3IgKGkgaW4gMTpsZW5ndGgoYU1fbGlzdCkpIHsNCiAgICBsZW4gPC0gYyhsZW4sIGxlbmd0aHMoYU1fbGlzdFtbaV1dKSkNCiAgfQ0KDQogIGN1dF9sZW4gPC0gcXVhbnRpbGUobGVuLCAwLjAyKQ0KDQogIGZvciAoaSBpbiAxOmxlbmd0aChhTV9saXN0KSkgew0KICAgIHJlbW92ZV9pbmQgPC0gd2hpY2gobGVuZ3RocyhhTV9saXN0W1tpXV0pIDwgY3V0X2xlbikNCiAgICByZW1vdmVfaW5kIDwtIGMocmVtb3ZlX2luZCwgMzAwMCkNCiAgICBhTV9saXN0W1tpXV0gPC0gYU1fbGlzdFtbaV1dWy1yZW1vdmVfaW5kXQ0KICAgIGFTX2xpc3RbW2ldXSA8LSBhU19saXN0W1tpXV1bLXJlbW92ZV9pbmRdDQogICAgYUxfbGlzdFtbaV1dIDwtIGFMX2xpc3RbW2ldXVstcmVtb3ZlX2luZF0NCiAgICB0X2xpc3RbW2ldXSA8LSB0X2xpc3RbW2ldXVstcmVtb3ZlX2luZF0NCiAgfQ0KDQogIGJhdGNoU2l6ZSA8LSBsZW5ndGhzKGFNX2xpc3QpDQoNCiAgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiAgYU1fbGlzdF9hbGwgPC0gZG8uY2FsbChjLCBhTV9saXN0KQ0KICBhU19saXN0X2FsbCA8LSBkby5jYWxsKGMsIGFTX2xpc3QpDQogIGFMX2xpc3RfYWxsIDwtIGRvLmNhbGwoYywgYUxfbGlzdCkNCiAgdF9hbGwgPC0gZG8uY2FsbChjLCB0X2xpc3QpDQogICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQogIGFNX21hdCA8LSBkby5jYWxsKHJiaW5kLCBhTV9saXN0X2FsbClbLDE6Y3V0X2xlbl0NCiAgYVNfbWF0IDwtIGRvLmNhbGwocmJpbmQsIGFTX2xpc3RfYWxsKVssMTpjdXRfbGVuXQ0KICBhTF9tYXQgPC0gZG8uY2FsbChyYmluZCwgYUxfbGlzdF9hbGwpWywxOmN1dF9sZW5dDQoNCiAgIyMjIyMjIyMjIyMjIyMjIyMjDQogIGFzc2lnbihwYXN0ZTAoInN1YiIsIGlkLCAiX2FNU0xUIiksDQogICAgICAgICBsaXN0KGFNYWc9YU1fbWF0LCBhU2FnPWFTX21hdCwgYUxhdD1hTF9tYXQsIHRpbWU9dF9hbGwsIGJhdGNoU2l6ZT1iYXRjaFNpemUpKQ0KfQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIFNhdmUNCnNhdmUoc3ViMV9hTVNMVCwgc3ViMl9hTVNMVCwgc3ViM19hTVNMVCwgc3ViNF9hTVNMVCwgc3ViNV9hTVNMVCwgc3ViNl9hTVNMVCwNCiAgICAgc3ViN19hTVNMVCwgc3ViOF9hTVNMVCwgc3ViOV9hTVNMVCwgc3ViMTBfYU1TTFQsIHN1YjExX2FNU0xULCBzdWIxMl9hTVNMVCwNCiAgICAgc3ViMTRfYU1TTFQsIHN1YjE1X2FNU0xULA0KDQogICAgIGZpbGU9Ii4uL0RhdGEvckRhdGEvc3ViR3NfZm9yX2RlcHRoLlJkYXRhIikNCmBgYA0KDQoNCiMgRGVwdGggQ2FsY3VsYXRpb24NCg0KVGhlIGRlcHRoIG1lYXN1cmUgaW52ZXN0aWdhdGVzIGhvdyBkZWVwIGEgZGF0YSBwb2ludCBpcyB3aXRoIHJlc3BlY3QgdG8gYSBzYW1wbGUuIEluIHVuaXZhcmlhdGUgZGF0YSB0aGUgbWVkaWFuIHJlcHJlc2VudHMgdGhlIGRlZXBlc3QgcG9pbnQgaW4gYSBjbG91ZCBvZiBwb2ludHMuIEluIGZ1bmN0aW9uYWwgZGF0YSBhbmFseXNpcyAoRkRBKSBkZXB0aCBtZWFzdXJlIGlzIGNvbW1vbmx5IHVzZWQgZm9yIGNsYXNzaWZpY2F0aW9uIGFuZCBjbHVzdGVyaW5nIG9mIGN1cnZlcy4gSW4gbXVsdGl2YXJpYXRlIGRhdGEgd2hlcmUgYSBudW1iZXIgb2YgZGlzY3JldGl6ZWQgY3VydmVzIGluIGEgY2VydGFpbiBudW1iZXIgb2YgcG9pbnRzICh1c3VhbGx5IHRpbWUgcG9pbnRzKSBhcmUgaW52ZXN0aWdhdGVkLCB0aGUgZGVwdGggbWVhc3VyZXMgdGhlIGNlbnRyYWxpdHkgb3Igb3V0bHlpbmduZXNzIG9mIGVhY2ggY3VydmUuIEhvd2V2ZXIsIGZvciBtdWx0aXZhcmlhdGUgZGF0YSB0aGVyZSBpcyBubyBvYnZpb3VzIHNpbXBsZSBtZXRob2QgZm9yIGRlcHRoIG1lYXN1cmVtZW50LiBNdWx0aXBsZSBkZXB0aCBtZWFzdXJlcyBoYXZlIGJlZW4gcHJvcG9zZWQgaW4gbGl0ZXJhdHVyZSBAY3VldmFzMjAwN3JvYnVzdC4gVGhlc2UgZGVwdGggdmFsdWVzIGFyZSBkZWZpbmVkIGJhc2VkIG9uIG1lZGlhbiBAZnJhaW1hbjIwMDF0cmltbWVkLCBUdWtleSBkZXB0aCwgcmFuZG9tIHByb2plY3Rpb25zIG9mIHRoZSBjdXJ2ZXMgYW5kIHRoZWlyIHByb2plY3Rpb25zLg0KDQpJbiB0aGUgY3VycmVudCB3b3JrIHdlIGhhdmUgZXhwbG9pdGVkIHRoZSBkZXB0aCBtZWFzdXJlIHRvIGludmVzdGlnYXRlIHRoZSBjZW50cmFsaXR5IG9mIHRoZSBnYWl0IGN5Y2xlIGFjY2VsZXJhdGlvbiBwcm9maWxlcyBpbiBvcmRlciB0byBmaW5kIHRoZWlyIGRldmlhdGlvbiBmcm9tIHRoZSBiYXNlbGluZSBkdWUgdG8gZmF0aWd1ZS4gQWZ0ZXIgaW52ZXN0aWdhdGluZyBtdWx0aXBsZSBkZXB0aCBtZWFzdXJlcyBAbGFtb29raTIwMjBjaGFsbGVuZ2VzLCB3ZSBoYXZlIG5hcnJvd2VkIHRoZW0gZG93biB0byAqKm1vZGUgZGVwdGgqKiBAQGN1ZXZhczIwMDdyb2J1c3QgYW5kICoqTXVsdGl2YXJpYXRlIGZ1bmN0aW9uYWwgaGFsZnNwYWNlIGRlcHRoIChNRkhEKSoqIEBodWJlcnQyMDEybmV3Lg0KDQojIyBNb2RlIERlcHRoDQoNCkluIHRoZSBmb2xsb3dpbmcgY29kZSBjaHVuayB3ZSBoYXZlIGNhbGN1bGF0ZWQgdGhlIG1vZGUgZGVwdGggZm9yIGVhY2ggZ2FpdCBjeWNsZSBhY2NlbGVyYXRpb24gcHJvZmlsZSB1c2luZyB0aGUgKmZkYS51c2MqIHBhY2thZ2UgaW4gUiBAZmVicmVybzIwMTJzdGF0aXN0aWNhbC4gVGhlIG1vZGUgZGVwdGggaXMgY2FsY3VsYXRlZCBmb3IgdGhlIGFjY2VsZXJhdGlvbiBtYWduaXR1ZGUgYnkgdXNpbmcgdGhlICQxXntzdH0kIDUwMCBnYWl0IGN5Y2xlcyBhcyB0aGUgYmFzZWxpbmUuIFRoZSBkZXB0aCBvZiB0aGUgcmVtYWluaW5nIGdhaXQgY3ljbGUgYWNjZWxlcmF0aW9uIHByb2ZpbGVzIGFyZSBjYWxjdWxhdGVkIGFnYWluc3QgdGhpcyBiYXNlbGluZS4NCg0KYGBge3IgbW9kZS1kZXB0aH0NCg0KbG9hZChmaWxlPSIuLi9EYXRhL3JEYXRhL3N1YkdzX2Zvcl9kZXB0aC5SZGF0YSIpDQoNCmZvciAoaWQgaW4gc2V0ZGlmZigxOjE1LCAxMykpIHsNCiAgDQogIGFNYWcgPC0gZ2V0KHBhc3RlMCgic3ViIiwgaWQsICJfYU1TTFQiKSkkYU1hZw0KICB0RXhwIDwtIGdldChwYXN0ZTAoInN1YiIsIGlkLCAiX2FNU0xUIikpJHRpbWUNCiAgDQogICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgTW9kZSBEZXB0aA0KICBpbkNvbnRyb2wgPC0gZmRhdGEoYU1hZ1sxOjUwMCxdKQ0KICBvbmxpbmUgPC0gZmRhdGEoYU1hZ1s1MDE6bnJvdyhhTWFnKSxdKQ0KICANCiAgaW5Db250cm9sRGVwdGggPC0ge2RlcHRoLm1vZGUoaW5Db250cm9sKX0kZGVwDQogIG9ubGluZURlcHRoIDwtIHtkZXB0aC5tb2RlKGZkYXRhb2JqPW9ubGluZSwgZmRhdGFvcmk9aW5Db250cm9sKX0kZGVwDQogIA0KICBtb2RlRGVwdGggPC0gYyhpbkNvbnRyb2xEZXB0aCwgb25saW5lRGVwdGgpDQogIA0KICBhc3NpZ24ocGFzdGUwKCJzdWIiLCBpZCwgIl9tb2RlX21hZyIpLA0KICAgICAgICAgbGlzdChtb2RlRGVwdGg9bW9kZURlcHRoLCB0RXhwPXRFeHApKQ0KfQ0KDQpzYXZlKHN1YjFfbW9kZV9tYWcsIHN1YjJfbW9kZV9tYWcsIHN1YjNfbW9kZV9tYWcsIHN1YjRfbW9kZV9tYWcsIHN1YjVfbW9kZV9tYWcsIHN1YjZfbW9kZV9tYWcsDQogICAgIHN1YjdfbW9kZV9tYWcsIHN1YjhfbW9kZV9tYWcsIHN1YjlfbW9kZV9tYWcsIHN1YjEwX21vZGVfbWFnLCBzdWIxMV9tb2RlX21hZywgc3ViMTJfbW9kZV9tYWcsIHN1YjE0X21vZGVfbWFnLCBzdWIxNV9tb2RlX21hZywNCiAgICAgZmlsZT0iLi4vRGF0YS9yRGF0YS9tb2RlX21hZy5SZGF0YSIpDQpgYGANCg0KIyMgVmlzdWFsaXplIHRoZSBNb2RlIERlcHRoIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCg0KVGhlIE1vZGUgZGVwdGggZm9yIGV2ZXJ5IGdhaXQgY3ljbGUgaXMgdmlzdWFsaXplZCBoZXJlIHdoZXJlIHRoZSByZWQgZGF0YSBwb2ludHMgcmVwcmVzZW50IHRoZSBiYXNlbGluZSBwcm9maWxlcy4NCg0KYGBge3IgdmlzX01vZGUsIGZpZy5hbGlnbj0iY2VudGVyIiwgcmVzdWx0cz0iYXNpcyIsIG91dC53aWR0aD0iMTAwJSJ9DQoNCmxvYWQoZmlsZT0iLi4vRGF0YS9yRGF0YS9tb2RlX21hZy5SZGF0YSIpDQoNCmZvciAoaWQgaW4gc2V0ZGlmZigxOjE1LCAxMykpIHsNCiAgZGVwdGhNb2RlIDwtIGdldChwYXN0ZTAoInN1YiIsIGlkLCAiX21vZGVfbWFnIikpJG1vZGVEZXB0aA0KICB0TW9kZSA8LSBnZXQocGFzdGUwKCJzdWIiLCBpZCwgIl9tb2RlX21hZyIpKSR0RXhwDQoNCiAgY2F0KCIjIyMiLCBwYXN0ZTAoIlN1YmplY3QiLCBpZCksICJ7LX0iLCdcbicpDQogIHBsb3QodE1vZGUsIGRlcHRoTW9kZSwgcGNoPTE2LCBjZXg9MSxjb2w9YyhyZXAoInJlZCIsIDUwMCksIHJlcCgiYmxhY2siLCBsZW5ndGgoZGVwdGhNb2RlKS01MDApKSkNCiAgbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZD1jKCJCYXNlbGluZSBkYXRhIiwgIk5ldyBkYXRhIiksIGNvbD1jKCJyZWQiLCAiYmxhY2siKSwgcGNoPTE2KQ0KICBjYXQoJ1xuIFxuJykNCg0KfQ0KDQpgYGANCg0KIyMgTXVsdGl2YXJpYXRlIEZ1bmN0aW9uYWwgSGFsZnNwYWNlIERlcHRoIChNRkhEKQ0KDQpUaGUgTUZIRCBkZXB0aCBpcyBpbnRyb2R1Y2VkIHRvIHByb3ZpZGUgb3JkZXJpbmcgb2YgY3VydmVzIGZyb20gdGhlIGNlbnRlciBvdXR3YXJkcyBmb3IgKiptdWx0aXZhcmlhdGUgY3VydmVzKiogQGh1YmVydDIwMTJuZXcuIEEgbXVsdGl2YXJpYXRlIGN1cnZlIGNvbnRhaW5zIGEgbXVsdGl2YXJpYXRlIHZlY3RvciBhdCBlYWNoIGRpc2NyZXRlIHRpbWUgcG9pbnQuIFRoZSBtZXRob2QgaXMgcGFydGljdWxhcmx5IHVzZWQgb24gbXVsdGl2YXJpYXRlIGN1cnZlcyB0aGF0IGFyZSBjb25zdHJ1Y3RlZCBieSBhZGRpbmcgdW5pdmFyaWF0ZSBjdXJ2ZXMgYW5kIHRoZWlyIGRlcml2YXRpdmVzLiBIb3dldmVyLCBpbiB0aGUgY3VycmVudCB3b3JrIHdlIGhhdmUgaW1wbGVtZW50ZWQgdGhlIG1ldGhvZCBvbiBtdWx0aXZhcmlhdGUgY3VydmVzIGNvbnN0cnVjdGVkIGJ5IHNhZ2l0dGFsIGFuZCBsYXRlcmFsIGdhaXQgYWNjZWxlcmF0aW9uIHByb2ZpbGVzLiBUaGUgJDFee3N0fSQgNTAwIGdhaXQgcHJvZmlsZXMgd2VyZSBjb25zaWRlcmVkIHRvIGJlIHRoZSBiYXNlbGluZSBkYXRhIHdoZXJlIHRoZSBwYXJ0aWNpcGFudCB3YXMgd2Fsa2luZyBmYXRpZ3VlLWZyZWUuIE5leHQsIGVhY2ggb2YgdGhlIHJlbWFpbmluZyBnYWl0IGFjY2VsZXJhdGlvbiBwcm9maWxlIHdhcyBhZGRlZCB0byB0aGUgYmFzZWxpbmUgcHJvZmlsZXMgYW5kIHRoZWlyIE1GSEQgZGVwdGggdmFsdWUgd2VyZSBjYWxjdWxhdGVkLg0KDQpUaGUgZm9sbG93aW5nIGNvZGUgY2h1bmsgaW5jbHVkZXMgdGhlIGNvZGUgdGhhdCB3ZSBoYXZlIHVzZWQgdG8gY2FsY3VsYXRlIHRoIE1GSEQgZGVwdGggdXNpbmcgdGhlIE1GSEQgZnVuY3Rpb24gaW4gUi4gU2luY2UgdGhlIE1GSEQgZGVwdGggY29tcHV0YXRpb25zIGFyZSBzaWduaWZpY2FudGx5IHRpbWUgY29uc3VtaW5nLCB3ZSBoYXZlIHBlcmZvcm1lZCB0aGUgY29tcHV0YXRpb25zIGluIHBhcmFsbGVsIG9uIGEgc3VwZXJjb21wdXRpbmcgY2x1c3RlciB3aXRoIDQwIGNvcmVzIGFuZCBzZXQgdGhlICpldmFsKiBvcHRpb24gdG8gKkZBTFNFKiBoZXJlLiBUaGUgb3V0cHV0IG9mIHRoaXMgY29kZSBjaHVuayBpcyBhZGRlZCB0byB0aGUgZm9sZGVyIFtEYXRhL3JEYXRhXShodHRwczovL2dpdGh1Yi5jb20vc2FlYnJhZ2FuaS9HYWl0X0VXTUEvdHJlZS9tYWluL0RhdGEvckRhdGEpLg0KDQpgYGB7ciBNRkhELWRlcHRoLCBldmFsPUZBTFNFfQ0KDQpsb2FkKGZpbGU9Ii4uL0RhdGEvckRhdGEvc3ViR3NfZm9yX2RlcHRoLlJkYXRhIikNCg0KY29yZXMgPSBkZXRlY3RDb3JlcygpIC0gMiAjIHRvIGdpdmUgdGhlIHNlcnZlciBzb21lIGJyZWF0aGluZyBSb29tDQpjbCA9IG1ha2VQU09DS2NsdXN0ZXIoY29yZXMpDQpyZWdpc3RlckRvUGFyYWxsZWwoY2wpDQoNCmZvciAoaWQgaW4gc2V0ZGlmZigxOjE1LCAxMykpIHsNCiAgDQogIGFTYWcgPC0gZ2V0KHBhc3RlMCgic3ViIiwgaWQsICJfYU1TTFQiKSkkYVNhZw0KICBhTGF0IDwtIGdldChwYXN0ZTAoInN1YiIsIGlkLCAiX2FNU0xUIikpJGFMYXQNCiAgdEV4cCA8LSBnZXQocGFzdGUwKCJzdWIiLCBpZCwgIl9hTVNMVCIpKSR0aW1lDQogIA0KICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIE1vZGUgRGVwdGgNCiAgaW5Db250cm9sU2FnIDwtIGFTYWdbMTo1MDAsXQ0KICBpbkNvbnRyb2xMYXQgPC0gYUxhdFsxOjUwMCxdDQogIGluQ29udHJvbERlcHRoIDwtIHtNRkhEKHkxPWluQ29udHJvbFNhZywgeTI9aW5Db250cm9sTGF0LCBhbHBoYT0wLjEyNSwgQmV0YT0wLjUpfSRNRkhEZGVwdGhbMSxdDQogIA0KICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIGZvcmVhY2gNCiAgb25saW5lRGVwdGggPC0gYygpDQogIGVuZCA8LSBucm93KGFTYWcpDQogIG9ubGluZURlcHRoIDwtIGZvcmVhY2goaT01MDE6ZW5kLCAucGFja2FnZXMgPSBjKCdmZGEudXNjJywgJ3RpZHl2ZXJzZScsICdNRkhEJyksDQogICAgICAgICAgICAgICAgICAgICAgICAgLmNvbWJpbmU9J2MnKSAlZG9wYXIlIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlbXAxIDwtIGFTYWcgJT4lIC5baSxdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0ZW1wMiA8LSBhTGF0ICU+JSAuW2ksXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBhcHBlbmQxIDwtIHJiaW5kKGluQ29udHJvbFNhZywgdGVtcDEpDQogICAgICAgICAgICAgICAgICAgICAgICAgICBhcHBlbmQyIDwtIHJiaW5kKGluQ29udHJvbExhdCwgdGVtcDIpDQogICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlbXAzIDwtIE1GSEQoeTE9YXBwZW5kMSwgeTI9YXBwZW5kMiwgYWxwaGE9MC4xMjUsIEJldGE9MC41KQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0ZW1wNCA8LSB0ZW1wMyRNRkhEZGVwdGggJT4lIC5bMSw1MDFdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0ZW1wNA0KICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgDQogIE1GSERkZXB0aCA8LSBjKGluQ29udHJvbERlcHRoLCBvbmxpbmVEZXB0aCkNCiAgDQogIGFzc2lnbihwYXN0ZTAoInN1YiIsIGlkLCAiX01GSEQiKSwNCiAgICAgICAgIGxpc3QoTUZIRGRlcHRoPU1GSERkZXB0aCwgdEV4cD10RXhwKSkNCn0NCg0Kc2F2ZShzdWIxX01GSEQsIHN1YjJfTUZIRCwgc3ViM19NRkhELCBzdWI0X01GSEQsIHN1YjVfTUZIRCwgc3ViNl9NRkhELCBzdWI3X01GSEQsIHN1YjhfTUZIRCwNCiAgICAgc3ViOV9NRkhELCBzdWIxMF9NRkhELCBzdWIxMV9NRkhELCBzdWIxMl9NRkhELCBzdWIxNF9NRkhELCBzdWIxNV9NRkhELA0KICAgICBmaWxlPSIuLi9EYXRhL3JEYXRhL01GSEQuUmRhdGEiKQ0KDQpgYGANCg0KIyMgVmlzdWFsaXplIHRoZSBNRkhEIERlcHRoIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCg0KQmVsb3cgd2UgaGF2ZSB2aXN1YWxpemVkIHRoZSBNRkhEIGRlcHRoIGZvciB0aGUgZ2FpdCBjeWNsZXMgd2hlcmUgdGhlIHJlZCBkYXRhIHBvaW50cyByZXByZXNlbnQgdGhlIGJhc2VsaW5lIGdhaXRzLg0KDQpgYGB7ciB2aXNfTUZIRCwgZmlnLmFsaWduPSJjZW50ZXIiLCByZXN1bHRzPSJhc2lzIiwgb3V0LndpZHRoPSIxMDAlIn0NCg0KbG9hZChmaWxlPSIuLi9EYXRhL3JEYXRhL01GSEQuUmRhdGEiKQ0KDQpmb3IgKGlkIGluIHNldGRpZmYoMToxNSwgMTMpKSB7DQogIGRlcHRoTUZIRCA8LSBnZXQocGFzdGUwKCJzdWIiLCBpZCwgIl9NRkhEIikpJE1GSERkZXB0aA0KICB0TUZIRCA8LSBnZXQocGFzdGUwKCJzdWIiLCBpZCwgIl9NRkhEIikpJHRFeHANCiAgDQogIGNhdCgiIyMjIiwgcGFzdGUwKCJTdWJqZWN0IiwgaWQpLCAiey19IiwnXG4nKQ0KICBwbG90KHRNRkhELCBkZXB0aE1GSEQsIHBjaD0xNiwgY2V4PTEsY29sPWMocmVwKCJyZWQiLCA1MDApLCByZXAoImJsYWNrIiwgbGVuZ3RoKGRlcHRoTUZIRCktNTAwKSkpDQogIGxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQ9YygiQmFzZWxpbmUgZGF0YSIsICJOZXcgZGF0YSIpLCBjb2w9YygicmVkIiwgImJsYWNrIiksDQogICAgICAgICBwY2g9MTYpDQogIGNhdCgnXG4gXG4nKQ0KICANCn0NCg0KYGBgDQoNCiMgRXhwb25lbnRpYWxseSBXZWlnaHRlZCBNb3ZpbmcgQXZlcmFnZSAoRVdNQSkNCg0KQ3VtdWxhdGl2ZSBTUEMgbWV0aG9kcyBhcmUgZGVzaWduZWQgdG8gZGV0ZWN0IHNtYWxsIGFuZCBwZXJzaXN0ZW50IGNoYW5nZXMgaW4gdGhlIGRhdGEgYnkgdXNpbmcgdGhlIG9ic2VydmVkIGRhdGEgYXZhaWxhYmxlIGF0IHRoZSBjdXJyZW50IHRpbWUgcG9pbnQuIFN1Y2ggbWV0aG9kcyB3b3JrIGJ5IGFjY3VtdWxhdGluZyB0aGUgc21hbGwgY2hhbmdlcyBpbiB0aGUgY29udHJvbGxlZCBzdGF0aXN0aWNzIHRoYXQgb2NjdXJyZWQgcHJpb3IgdG8gdGhlIHByZXNlbnQgdGltZSBwb2ludC4gU2luY2Ugd2UgZXhwZWN0ZWQgdGhlIGdhaXQgZGF0YSBpbiBvdXIgc3R1ZHkgdG8gY2hhbmdlIGdyYWR1YWxseSwgd2UgdXNlZCB0aGUgZXhwb25lbnRpYWxseSB3ZWlnaHRlZCBtb3ZpbmcgYXZlcmFnZSAoRVdNQSkgU1BDIG1ldGhvZCB0byBtb25pdG9yIGZhdGlndWUgZHVyaW5nIHRoZSBzaW11bGF0ZWQgTU1IIHNlc3Npb25zLg0KDQojIyBFV01BIG9uIFN1Ymdyb3VwIE1vZGUgRGVwdGggVmFsdWVzIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCg0KQWZ0ZXIgY2FsY3VsYXRpbmcgdGhlIE1vZGUgZGVwdGggdmFsdWVzIGZvciBpbmRpdmlkdWFsIGdhaXQgY3ljbGVzIHdlIHJlYXNzaWduZWQgdGhlIGRlcHRoIHZhbHVlcyB0byB0aGVpciBvcmlnaW5hbCBzdWJncm91cHMgaW4gdGhlIGZvbGxvd2luZyBjb2RlIGNodW5rLiBOZXh0LCB3ZSB1c2VkIHRoZSAqcWNjKiBwYWNrYWdlIGluIFIgYW5kIGNvbnN0cnVjdGVkIHRoZSBFV01BIGNvbnRyb2wgY2hhcnQgYnkgY29uc2lkZXJpbmcgdGhlICQxXntzdH0kIDEwIHN1Ymdyb3VwcyBhcyB0aGUgaW4tY29udHJvbCBkYXRhIGFuZCBwcmVzZW50ZWQgdGhlIGNvbnRyb2wgY2hhcnRzLg0KDQpgYGB7ciBFV01BX01vZGUsIGZpZy5hbGlnbj0iY2VudGVyIiwgcmVzdWx0cz0iYXNpcyIsIG91dC53aWR0aD0iMTAwJSJ9DQoNCmxvYWQoZmlsZT0iLi4vRGF0YS9yRGF0YS9zdWJHc19mb3JfZGVwdGguUmRhdGEiKQ0KbG9hZChmaWxlPSIuLi9EYXRhL3JEYXRhL21vZGVfbWFnLlJkYXRhIikNCmFhIDwtIDMNCmZvciAoaWQgaW4gc2V0ZGlmZigxOjE1LCAxMykpIHsNCiAgDQogIGRlcF9Nb2RlIDwtIGdldChwYXN0ZTAoInN1YiIsIGlkLCAiX21vZGVfbWFnIikpJG1vZGVEZXB0aA0KICBiYXRjaFNpemUgPC0gZ2V0KHBhc3RlMCgic3ViIiwgaWQsICJfYU1TTFQiKSkkYmF0Y2hTaXplDQogIA0KICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgcmVhc3NpZ24gZGVwdGggdmFsdWVzIHRvIG9yaWdpbmFsIHN1Ykdyb3Vwcw0KICBkZXBNb2RlX3NwbGl0IDwtIHNhcHBseSgxOmxlbmd0aChiYXRjaFNpemUpLCBGVU49ZnVuY3Rpb24oeCl7cmVwKHgsIGJhdGNoU2l6ZVt4XSl9KSAlPiUgZG8uY2FsbCh3aGF0PWMpICU+JSBzcGxpdCh4PWRlcF9Nb2RlKQ0KICANCiAgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIENyZWF0ZSBtYXRyaXggd2l0aCBOQSdzIGF0IHRoZSBlbmQgb2Ygc21hbGxlciBiYXRjaGVzDQogIE1vZGVfYmF0Y2hfbWF0IDwtIHNhcHBseSgxOmxlbmd0aChkZXBNb2RlX3NwbGl0KSwgRlVOPWZ1bmN0aW9uKHgpe2MoZGVwTW9kZV9zcGxpdFtbeF1dLCByZXAoTkEsIG1heChiYXRjaFNpemUpIC0gbGVuZ3RoKGRlcE1vZGVfc3BsaXRbW3hdXSkpKX0pICU+JSB0KCkNCiAgDQogICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyBSZW1vdmUgc21hbGwgbGVuZ3RoIGJhdGNoZXMNCiAgcm1faW5kIDwtIHdoaWNoKGFwcGx5KE1vZGVfYmF0Y2hfbWF0LCAxLCBmdW5jdGlvbih4KSBzdW0oIWlzLm5hKHgpKSkgPCAxMCkNCiAgcm1faW5kIDwtIGMocm1faW5kLCAzMDAwKQ0KICANCiAgTW9kZV9iYXRjaF9tYXQgPC0gTW9kZV9iYXRjaF9tYXRbLXJtX2luZCxdDQogIA0KICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgUGxvdCBNRkhEIEVXTUEgZm9yIHdhbGtpbmcgY3ljbGUgYmF0Y2hlcw0KICBMYW1iZGEgPC0gMC41DQogIGVuZCA8LSBucm93KE1vZGVfYmF0Y2hfbWF0KQ0KICBJQ19sZW4gPC0gMTANCiAgDQogIGV3bWExIDwtIGV3bWEoTW9kZV9iYXRjaF9tYXRbMTpJQ19sZW4sXSwgbGFtYmRhPUxhbWJkYSwgbnNpZ21hcz01LCBuZXdkYXRhPU1vZGVfYmF0Y2hfbWF0WyhJQ19sZW4rMSk6ZW5kLF0sIGFkZC5zdGF0cz1GLCBwbG90PUYpDQogIA0KICBjYXQoIiMjIyIsIHBhc3RlMCgiU3ViamVjdCIsIGlkKSwgInstfSIsJ1xuJykNCiAgcGxvdChld21hMSwNCiAgICAgICB0aXRsZT1wYXN0ZTAoInN1YmplY3QgIiwgaWQsICJcbkVXTUEgQ2hhcnQgb24gTW9kZSBEZXB0aCIpLCB4bGFiPSJTdWJncm91cCBJbmRleCIpDQogIGNhdCgnXG4gXG4nKQ0KfQ0KYGBgDQoNCg0KIyMgRVdNQSBvbiBTdWJncm91cCBFV01BIERlcHRoIFZhbHVlcyB7LnRhYnNldCAudGFic2V0LWZhZGV9DQoNCkluIHRoZSBmb2xsb3dpbmcgY29kZSBjaHVuayB3ZSBoYXZlIGNvbnN0cnVjdGVkIHRoZSBFV01BIGNvbnRyb2wgY2hhcnRzIG9uIHRoZSBNRkhEIGRlcHRoIHZhbHVlcyB1c2luZyB0aGUgKnFjYyogcGFja2FnZSBpbiBSIGFuZCBwcmVzZW50ZWQgdGhlIGNoYXJ0cy4NCg0KYGBge3IgRVdNQV9NRkhELCBmaWcuYWxpZ249ImNlbnRlciIsIHJlc3VsdHM9ImFzaXMiLCBvdXQud2lkdGg9IjEwMCUifQ0KDQpsb2FkKGZpbGU9Ii4uL0RhdGEvckRhdGEvc3ViR3NfZm9yX2RlcHRoLlJkYXRhIikNCmxvYWQoZmlsZT0iLi4vRGF0YS9yRGF0YS9NRkhELlJkYXRhIikNCg0KYWEgPC0gMg0KDQpmb3IgKGlkIGluIHNldGRpZmYoMToxNSwgMTMpKSB7DQogIA0KICBkZXBfTUZIRCA8LSBnZXQocGFzdGUwKCJzdWIiLCBpZCwgIl9NRkhEIikpJE1GSERkZXB0aA0KICBiYXRjaFNpemUgPC0gZ2V0KHBhc3RlMCgic3ViIiwgaWQsICJfYU1TTFQiKSkkYmF0Y2hTaXplDQogIA0KICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgcmVhc3NpZ24gZGVwdGggdmFsdWVzIHRvIG9yaWdpbmFsIHN1Ykdyb3Vwcw0KICBkZXBNRkhEX3NwbGl0IDwtIHNhcHBseSgxOmxlbmd0aChiYXRjaFNpemUpLCBGVU49ZnVuY3Rpb24oeCl7cmVwKHgsIGJhdGNoU2l6ZVt4XSl9KSAlPiUgZG8uY2FsbCh3aGF0PWMpICU+JSBzcGxpdCh4PWRlcF9NRkhEKQ0KICANCiAgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIENyZWF0ZSBtYXRyaXggd2l0aCBOQSdzIGF0IHRoZSBlbmQgb2Ygc21hbGxlciBiYXRjaGVzDQogIE1GSERfYmF0Y2hfbWF0IDwtIHNhcHBseSgxOmxlbmd0aChkZXBNRkhEX3NwbGl0KSwgRlVOPWZ1bmN0aW9uKHgpe2MoZGVwTUZIRF9zcGxpdFtbeF1dLCByZXAoTkEsIG1heChiYXRjaFNpemUpIC0gbGVuZ3RoKGRlcE1GSERfc3BsaXRbW3hdXSkpKX0pICU+JSB0KCkNCiAgDQogICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyBSZW1vdmUgc21hbGwgbGVuZ3RoIGJhdGNoZXMNCiAgcm1faW5kIDwtIHdoaWNoKGFwcGx5KE1GSERfYmF0Y2hfbWF0LCAxLCBmdW5jdGlvbih4KSBzdW0oIWlzLm5hKHgpKSkgPCAxMCkNCiAgcm1faW5kIDwtIGMocm1faW5kLCAzMDAwKQ0KICANCiAgTUZIRF9iYXRjaF9tYXQgPC0gTUZIRF9iYXRjaF9tYXRbLXJtX2luZCxdDQogIA0KICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgUGxvdCBNRkhEIEVXTUEgZm9yIHdhbGtpbmcgY3ljbGUgYmF0Y2hlcw0KICBMYW1iZGEgPC0gMC41DQogIGVuZCA8LSBucm93KE1GSERfYmF0Y2hfbWF0KQ0KICBJQ19sZW4gPC0gMTANCiAgDQogIGV3bWExIDwtIGV3bWEoTUZIRF9iYXRjaF9tYXRbMTpJQ19sZW4sXSwgbGFtYmRhPUxhbWJkYSwgbnNpZ21hcz01LCBuZXdkYXRhPU1GSERfYmF0Y2hfbWF0WyhJQ19sZW4rMSk6ZW5kLF0sIGFkZC5zdGF0cz1GLCBwbG90PUYpDQogIA0KICBjYXQoIiMjIyIsIHBhc3RlMCgiU3ViamVjdCIsIGlkKSwgInstfSIsJ1xuJykNCiAgcGxvdChld21hMSwNCiAgICAgICB0aXRsZT1wYXN0ZTAoInN1YmplY3QgIiwgaWQsICJcbkVXTUEgQ2hhcnQgb24gTUZIRCBEZXB0aCIpLCB4bGFiPSJTdWJncm91cCBJbmRleCIpDQogIGNhdCgnXG4gXG4nKQ0KfQ0KYGBgDQoNCi0tLQ0KDQojIFJlZmVyZW5jZXMgey19DQo=